package com.yhy.wxzn.wcs.wcsWmsCommunication;


import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.yhy.wxzn.wcs.aop.InterfaceLog;
import com.yhy.wxzn.wcs.device.plc.task.PlcTaskThread;
import com.yhy.wxzn.wcs.device.rgv.bean.Coord;
import com.yhy.wxzn.wcs.device.rgv.bean.Node;
import com.yhy.wxzn.wcs.device.rgv.enums.NodeStatus;
import com.yhy.wxzn.wcs.device.rgv.enums.NodeType;
import com.yhy.wxzn.wcs.device.rgv.enums.RgvProgress;
import com.yhy.wxzn.wcs.entity.*;
import com.yhy.wxzn.wcs.enums.PlcTaskProgress;
import com.yhy.wxzn.wcs.enums.RgvTaskAssignStatus;
import com.yhy.wxzn.wcs.enums.TaskStatus;
import com.yhy.wxzn.wcs.enums.TaskType;
import com.yhy.wxzn.wcs.service.impl.*;
import com.yhy.wxzn.wcs.wcsWmsCommunication.bean.*;
import com.yhy.wxzn.wcs.wcsWmsCommunication.dto.CargoNoDto;
import com.yhy.wxzn.wcs.wcsWmsCommunication.dto.NodeDto;
import com.yhy.wxzn.wcs.wcsWmsCommunication.dto.NodeListRequest;
import com.yhy.wxzn.wcs.wcsWmsCommunication.dto.WmsResonsesNodeDto;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.util.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@RestController
@RequestMapping("/fromWms")
public class WmsConnectController {


    @Autowired
    private WcsTaskServiceImpl taskService;


    @Autowired
    private WcsPortServiceImpl portService;


    @Autowired
    private WcsInboundConfigServiceImpl inboundConfigService;


    @Autowired
    private WcsOutboundConfigServiceImpl outboundConfigService;


    @Autowired
    private WcsMapInfoServiceImpl mapInfoService;


    @Autowired
    private WcsDeviceInfoServiceImpl deviceInfoService;


    @ApiOperation(value = "任务接收")
    @PostMapping("/taskReceive")
    @InterfaceLog(value = "任务接收", sender = "WMS", recipient = "WCS")
    public WmsResponses taskReceive(@RequestBody WmsTask wmsTask) {
        log.info("接收到WMS下发的任务:" + JSON.toJSONString(wmsTask));
        try {
            ArrayList<WcsTask> taskDBs = new ArrayList<>();
            int priorityCode = 1;
            if (wmsTask.getPriorityCode() != null) {
                priorityCode = wmsTask.getPriorityCode();
            }
            List<WmsTask.TasksBean> tasks = wmsTask.getTasks();
            Collections.sort(tasks);
            for (WmsTask.TasksBean tasksBean : tasks) {
                WmsResponses wmsResponses = baseCheck(tasksBean);
                if (wmsResponses != null) {
                    return wmsResponses;
                }

                taskDBs.add(normalTaskReceive(tasksBean, priorityCode));
            }


            String res = taskService.insertTask(taskDBs);
            if (StrUtil.isEmpty(res)) {
                startPlcTask(taskDBs);
                return WmsResponses.returnSuccess();
            }else {
                return WmsResponses.returnError(res);
            }
        } catch (Exception e) {
            log.error(e.getMessage(),e);
            return WmsResponses.returnError(e.getMessage());
        }
    }

    private void startPlcTask(ArrayList<WcsTask> taskDBs) {
        List<WcsTask> wcsTasks = taskDBs.stream().filter(task -> task.getTaskType() == TaskType.IN).collect(Collectors.toList());
        for (WcsTask wcsTask : wcsTasks) {
            PlcTaskThread.executeTask(wcsTask);
        }
    }


    /**
     * 正常出入库任务
     * @param tasksBean
     * @param priorityCode
     * @return
     */
    private WcsTask normalTaskReceive(WmsTask.TasksBean tasksBean, int priorityCode) {
        WcsTask taskdb = new WcsTask();
        // 根据WMS发送的任务起点和终点找到小车的起点和终点
        Node rgvStartNode;
        Node rgvEndNode;
        try {
            rgvStartNode = getRgvStartNode(tasksBean);
        } catch (Exception e) {
            log.error("查找起点出现异常", e);
            throw new WmsTaskReceiveException(TaskReceiveResultEnum.TASK_START_HAS_ERROR.getReturnStatus(),
                    e.getMessage() + TaskReceiveResultEnum.TASK_START_HAS_ERROR.getDescription() + tasksBean.getTaskId());
        }
        try {
            rgvEndNode = getRgvEndNode(tasksBean);
        } catch (Exception e) {
            log.error("查找终点出现异常", e);
            throw new WmsTaskReceiveException(TaskReceiveResultEnum.TASK_END_HAS_ERROR.getReturnStatus(),
                    e.getMessage() + TaskReceiveResultEnum.TASK_END_HAS_ERROR.getDescription() + tasksBean.getTaskId());
        }

        checkRgvStartAndEnd(tasksBean,rgvStartNode,rgvEndNode);

        taskdb.setWmsTaskId(tasksBean.getTaskId());
        taskdb.setTaskType(TaskType.parseTaskType(tasksBean.getTaskType()));
        taskdb.setTaskStatus(TaskStatus.NEW);
        taskdb.setStart(rgvStartNode.toString());
        taskdb.setEnd(rgvEndNode.toString());
        taskdb.setBarCode(tasksBean.getBarCode());
        taskdb.setAssignStatus(RgvTaskAssignStatus.UNASSIGNED);
        taskdb.setProcessRgvDeviceCode(null);
        taskdb.setRgvProgress(RgvProgress.NOT_ASSIGNED_TO_RGV);
        taskdb.setPriorityCode(priorityCode);
        taskdb.setPlcProgress(PlcTaskProgress.ALL_DEVICE_COMPLETE.getTaskProgress());

        if (tasksBean.getTaskType() == TaskType.IN.getTypeNum()) {
            taskdb.setPortCode(tasksBean.getStartNode());
            taskdb.setPlcProgress(PlcTaskProgress.WAIT.getTaskProgress());
        } else if (tasksBean.getTaskType() == TaskType.OUT.getTypeNum()) {
            taskdb.setPortCode(tasksBean.getEndNode());
            taskdb.setPlcProgress(PlcTaskProgress.WAIT.getTaskProgress());
        }
        taskdb.setCreateTime(new Date());
        return taskdb;
    }

    private void checkRgvStartAndEnd(WmsTask.TasksBean tasksBean, Node rgvStartNode, Node rgvEndNode) {
        if (rgvStartNode == null) {
            throw new WmsTaskReceiveException(TaskReceiveResultEnum.TASK_NO_START.getReturnStatus(),
                    TaskReceiveResultEnum.TASK_NO_START.getDescription() + tasksBean.getTaskId());
        }

        if (rgvEndNode == null) {
            throw new WmsTaskReceiveException(TaskReceiveResultEnum.TASK_NO_END.getReturnStatus(),
                    TaskReceiveResultEnum.TASK_NO_END.getDescription() + tasksBean.getTaskId());
        }


        if (tasksBean.getTaskType() == TaskType.TRANSFER.getTypeNum()
                && rgvStartNode.getNodeStatus().equals(NodeStatus.N) && NodeType.P.equals(rgvStartNode.getNodeType())) {
            throw new WmsTaskReceiveException(TaskReceiveResultEnum.TASK_TRANSFER_START_NO_GOODS.getReturnStatus(),
                    TaskReceiveResultEnum.TASK_TRANSFER_START_NO_GOODS.getDescription() + tasksBean.getTaskId());
        }
        if (tasksBean.getTaskType() == TaskType.TRANSFER.getTypeNum()
                && rgvEndNode.getNodeStatus().equals(NodeStatus.Y)) {
            throw new WmsTaskReceiveException(TaskReceiveResultEnum.TASK_TRANSFER_END_HAS_GOODS.getReturnStatus(),
                    TaskReceiveResultEnum.TASK_TRANSFER_END_HAS_GOODS.getDescription() + tasksBean.getTaskId());
        }
        if (tasksBean.getTaskType() == TaskType.IN.getTypeNum()
                && rgvEndNode.getNodeStatus().equals(NodeStatus.Y)) {
            throw new WmsTaskReceiveException(TaskReceiveResultEnum.TASK_IN_END_HAS_GOODS.getReturnStatus(),
                    TaskReceiveResultEnum.TASK_IN_END_HAS_GOODS.getDescription() + tasksBean.getTaskId());
        }
        if (tasksBean.getTaskType() == TaskType.OUT.getTypeNum()
                && rgvStartNode.getNodeStatus().equals(NodeStatus.N)) {
            throw new WmsTaskReceiveException(TaskReceiveResultEnum.TASK_OUT_START_NO_GOODS.getReturnStatus(),
                    TaskReceiveResultEnum.TASK_OUT_START_NO_GOODS.getDescription() + tasksBean.getTaskId());
        }
    }

    /**
     * 从WMS接受到的任务做基础校验
     *
     * @param tasksBean
     * @return
     */
    private WmsResponses baseCheck(WmsTask.TasksBean tasksBean) {
        if (tasksBean == null || tasksBean.getTaskId() == null) {
            return WmsResponses.returnError(TaskReceiveResultEnum.TASK_ID_IS_NULL.getReturnStatus(), TaskReceiveResultEnum.TASK_ID_IS_NULL.getDescription());
        }
        if (tasksBean.getTaskId() == null || StringUtils.isEmpty(tasksBean.getTaskId())) {
            return WmsResponses.returnError(TaskReceiveResultEnum.TASK_ID_IS_NULL.getReturnStatus(), TaskReceiveResultEnum.TASK_ID_IS_NULL.getDescription());
        }
        if (taskService.queryTaskByWmsId(tasksBean.getTaskId()) != null) {
            return WmsResponses.returnError(TaskReceiveResultEnum.TASK_ID_EXIST.getReturnStatus(), TaskReceiveResultEnum.TASK_ID_EXIST.getDescription() + tasksBean.getTaskId());
        }
        if (tasksBean.getTaskType() == null) {
            return WmsResponses.returnError(TaskReceiveResultEnum.TASK_TYPE_EXCEPTION.getReturnStatus(),
                    TaskReceiveResultEnum.TASK_TYPE_EXCEPTION.getDescription() + tasksBean.getTaskId());
        }
        if (StringUtils.isEmpty(tasksBean.getStartNode())) {
            return WmsResponses.returnError(TaskReceiveResultEnum.TASK_NO_START.getReturnStatus(),
                    TaskReceiveResultEnum.TASK_NO_START.getDescription() + tasksBean.getTaskId());
        }
        if (StringUtils.isEmpty(tasksBean.getEndNode())) {
            return WmsResponses.returnError(TaskReceiveResultEnum.TASK_NO_END.getReturnStatus(),
                    TaskReceiveResultEnum.TASK_NO_END.getDescription() + tasksBean.getTaskId());
        }
        if (tasksBean.getTaskType() == TaskType.IN.getTypeNum()) {

            WcsPort portDB = portService.getByPortCode(tasksBean.getStartNode());
            if (portDB == null) {
                return WmsResponses.returnError(TaskReceiveResultEnum.NO_IN_PORT.getReturnStatus(),
                        TaskReceiveResultEnum.NO_IN_PORT.getDescription() + tasksBean.getTaskId());
            }
        }
        if (tasksBean.getTaskType() == TaskType.OUT.getTypeNum()) {
            WcsPort portDB = portService.getByPortCode(tasksBean.getEndNode());
            if (portDB == null) {
                return WmsResponses.returnError(TaskReceiveResultEnum.NO_OUT_PORT.getReturnStatus(),
                        TaskReceiveResultEnum.NO_OUT_PORT.getDescription() + tasksBean.getTaskId());
            }
        }
        if (tasksBean.getTaskType() != TaskType.IN.getTypeNum() && mapInfoService.getNodeDBByWmsCoord(tasksBean.getStartNode()) == null) {
            return WmsResponses.returnError(TaskReceiveResultEnum.TASK_START_NOT_EXIST.getReturnStatus(),
                    TaskReceiveResultEnum.TASK_START_NOT_EXIST.getDescription() + tasksBean.getTaskId());
        }
        if (tasksBean.getTaskType() != TaskType.OUT.getTypeNum() && mapInfoService.getNodeDBByWmsCoord(tasksBean.getEndNode()) == null) {
            return WmsResponses.returnError(TaskReceiveResultEnum.TASK_END_NOT_EXIST.getReturnStatus(),
                    TaskReceiveResultEnum.TASK_END_NOT_EXIST.getDescription() + tasksBean.getTaskId());
        }

        try {
            TaskType.parseTaskType(tasksBean.getTaskType());
        } catch (Exception e) {
            return WmsResponses.returnError(TaskReceiveResultEnum.TASK_TYPE_EXCEPTION.getReturnStatus(),
                    TaskReceiveResultEnum.TASK_TYPE_EXCEPTION.getDescription() + e.getMessage() + tasksBean.getTaskId());
        }
        return null;
    }


    /**
     * 根据WMS发送的任务获取小车的起点
     */
    private Node getRgvStartNode(WmsTask.TasksBean tasksBean) {
        if (tasksBean.getTaskType().equals(TaskType.IN.getTypeNum())) {
            String portCode = tasksBean.getStartNode();
            Coord endNode = mapInfoService.parseNode(mapInfoService.getNodeDBByWmsCoord(tasksBean.getEndNode()));

            WcsInboundConfig inboundConfig = inboundConfigService.getByPortCode(portCode);
            String connectCoordStrs = inboundConfig.getConnectCoord();

            Node node = Arrays.stream(connectCoordStrs.split(";"))
                    .map(c -> Node.parseNode(c)).filter(c -> c.getZ() == endNode.getZ()).findFirst().get();

            if (node == null) throw new RuntimeException("入库任务wcs无法获取RGV起点，请检查方案接驳点配置");

            return mapInfoService.getNode(node);
        } else {
            return mapInfoService.parseNode(mapInfoService.getNodeDBByWmsCoord(tasksBean.getStartNode()));
        }
    }

    /**
     * 根据WMS发送的任务获取小车的终点
     */
    private Node getRgvEndNode(WmsTask.TasksBean tasksBean) {
        if (tasksBean.getTaskType().equals(TaskType.OUT.getTypeNum())) {
            Coord startNode =  mapInfoService.parseNode(mapInfoService.getNodeDBByWmsCoord(tasksBean.getStartNode()));
            String portCode = tasksBean.getEndNode();

            WcsOutboundConfig outboundConfig = outboundConfigService.getByPortCode(portCode);
            String connectCoordStrs = outboundConfig.getConnectCoord();

            Node node = Arrays.stream(connectCoordStrs.split(";"))
                    .map(c -> Node.parseNode(c)).filter(c -> c.getZ() == startNode.getZ()).findFirst().get();

            if (node == null) throw new RuntimeException("出库任务wcs无法获取RGV起点，请检查方案接驳点配置");

            return mapInfoService.getNode(node);
        } else {
            return mapInfoService.parseNode(mapInfoService.getNodeDBByWmsCoord(tasksBean.getEndNode()));
        }
    }


    @PostMapping("/cargoNoSyn")
    @ResponseBody
    @ApiOperation(value = "货位编码同步")
    @InterfaceLog(value = "货位编码同步", sender = "WMS", recipient = "WCS")
    public WmsResponses cargoNoSyn(@RequestBody List<CargoNoDto> cargoNoDtos) {
        log.info("接收到上位系统<货位编码同步>请求，参数-->" + JSON.toJSONString(cargoNoDtos));
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        List<WcsMapInfo> wcsMapInfos = mapInfoService.list();
        HashMap<Node, NodeInfo> map = new HashMap<>();

        for (WcsMapInfo wcsMapInfo : wcsMapInfos) {
            Node node = mapInfoService.parseNode(wcsMapInfo);
            map.put(node,new NodeInfo(node,wcsMapInfo.getId()));
        }
        List<WcsMapInfo> nodeDBList = new ArrayList<>();

        for (CargoNoDto cargoNoDto : cargoNoDtos) {
            // 过滤
            NodeInfo nodeInfo = map.get(new Coord(cargoNoDto.getPosiX(), cargoNoDto.getPosiY(), cargoNoDto.getPosiZ()));
            if (nodeInfo == null) {
                failureNum++;
                failureMsg.append("<br/>" + failureNum + "、货位 " + cargoNoDto.getCargoNo() + " 不存在");
            }  else {

                if (!nodeInfo.getNode().getNodeType().equals(NodeType.P)) {
                    successMsg.append("<br/>" + failureNum + "、货位 " + cargoNoDto.getCargoNo() + " 在WCS不是货位！");
                }
                WcsMapInfo mapInfo = new WcsMapInfo();
                mapInfo.setId(nodeInfo.getNodeId());
                mapInfo.setCoord(cargoNoDto.getCargoNo());
                nodeDBList.add(mapInfo);
            }
        }

        if (failureNum > 0) {
            failureMsg.insert(0, "很抱歉，导入失败！共 " + failureNum + " 条数据无法同步，错误如下：");
            return WmsResponses.returnError(failureMsg.toString());
        }
        boolean b;
        try {
             b = mapInfoService.updateBatchById(nodeDBList, nodeDBList.size());
        }catch (Exception e){
            return WmsResponses.returnError("保存数据库失败:"+e.getMessage());
        }

        if (b){
            successMsg.insert(0, "恭喜您，数据已全部同步成功！共 " + nodeDBList.size() + " 条");
            return WmsResponses.returnSuccess(successMsg.toString());
        }else {
            return WmsResponses.returnError("保存数据库失败");
        }
    }

    @AllArgsConstructor
    @Data
    static class NodeInfo{
        Node node;
        String nodeId;
    }


    /**
     * 货位信息查询
     * @param nodeListRequest
     * @return
     */
    @PostMapping("/cargoLocationStatus")
    @ResponseBody
    @ApiOperation(value = "货位状态查询")
    @InterfaceLog(value = "货位状态查询", sender = "WMS", recipient = "WCS")
    public WmsResonsesNodeDto cargoLocationStatus(@RequestBody NodeListRequest nodeListRequest) {
        log.info("接收到上位系统<货位状态查询>请求，参数-->" + JSON.toJSONString(nodeListRequest));
        Integer layer = nodeListRequest.getLayer();
        String cargoLocationId = nodeListRequest.getCargoLocationId();
        String returnInfo = "";
        List<NodeDto> nodeDtoList =new ArrayList<>();
        if (layer == null) {
            returnInfo = "查询失败，请填写当前层";
            return WmsResonsesNodeDto.returnError(returnInfo, nodeDtoList);
        }

        if (StringUtils.isBlank(cargoLocationId)) {
            List<WcsMapInfo> wcsMapInfos = mapInfoService.getWcsMapInfoByFloorAndType(NodeType.P, layer);
            if (!CollectionUtils.isEmpty(wcsMapInfos)) {
                for (WcsMapInfo wcsMapInfo: wcsMapInfos) {
                    NodeDto nodeDto = new NodeDto();
                    nodeDto.setCargoLocationId(wcsMapInfo.getCoord());
                    nodeDto.setCargoLocationStatus(wcsMapInfo.getNodeStatus());
                    nodeDto.setPalletCode(wcsMapInfo.getPalletCode());
                    nodeDtoList.add(nodeDto);
                }
                returnInfo = "查询第" + layer + "层货位信息成功";
            }
        } else {
            WcsMapInfo wcsMapInfo = mapInfoService.getNodeDBByWmsCoord(cargoLocationId);
            if (wcsMapInfo == null) {
                returnInfo = "查询失败，货位编码为：" + cargoLocationId + "未找到";
                return WmsResonsesNodeDto.returnError(returnInfo, nodeDtoList);
            } else {
                NodeDto nodeDto = new NodeDto();
                nodeDto.setCargoLocationId(wcsMapInfo.getCoord());
                nodeDto.setCargoLocationStatus(wcsMapInfo.getNodeStatus());
                nodeDto.setPalletCode(wcsMapInfo.getPalletCode());
                nodeDtoList.add(nodeDto);
                returnInfo = "查询成功";
            }
        }
        return WmsResonsesNodeDto.returnSuccess(returnInfo, nodeDtoList);
    }

    /**
     * 货位信息同步
     * @param cargos
     * @return
     */
    @PostMapping("/cargoLocationSyn")
    @ApiOperation(value = "货位信息同步")
    @InterfaceLog(value = "货位信息同步", sender = "WMS", recipient = "WCS")
    public CargoRtnMsg cargoLocationSyn(@RequestBody List<CargoFromWms> cargos) {
        List<WcsMapInfo> mapInfos = new ArrayList<>();
        CargoRtnMsg cargoRtnMsg = new CargoRtnMsg();
        int failureNum = 0;
        StringBuffer successMsg = new StringBuffer();
        StringBuffer failureMsg = new StringBuffer();
        for (CargoFromWms cargoFromWMS : cargos) {
            String cargoLocationStatus = cargoFromWMS.getCargoLocationStatus();
            String trim = cargoLocationStatus.trim();
            WcsMapInfo wcsMapInfo;
            if (!"Y".equals(trim) &&  !"N".equals(trim)){
                failureNum++;
                failureMsg.append("<br/>" + trim + "是错误的货物状态.");
            }else if ((wcsMapInfo = mapInfoService.getNodeDBByWmsCoord(cargoFromWMS.getCargoLocationId())) == null) {
                failureNum++;
                failureMsg.append("<br/>" + cargoFromWMS.getCargoLocationId() + "不存在.");
            } else {
                WcsMapInfo wcsMapInfo1 = new WcsMapInfo();
                wcsMapInfo1.setId(wcsMapInfo.getId());
                wcsMapInfo1.setNodeStatus(cargoFromWMS.getCargoLocationStatus());
                mapInfos.add(wcsMapInfo1);
            }
        }

        if (failureNum > 0) {
            cargoRtnMsg.setReturnStatus(1);
            failureMsg.insert(0, ",很抱歉，导入失败！");
            cargoRtnMsg.setReturnInfo(failureMsg.toString());
            cargoRtnMsg.setMsgTime(new Date());
            return cargoRtnMsg;
        }


        try {
            if (!CollectionUtils.isEmpty(mapInfos))  mapInfoService.updateBatchById(mapInfos, mapInfos.size());
        }catch (Exception e){
            cargoRtnMsg.setReturnStatus(1);
            cargoRtnMsg.setReturnInfo(e.getMessage());
            cargoRtnMsg.setMsgTime(new Date());
            return cargoRtnMsg;
        }

        cargoRtnMsg.setReturnStatus(0);
        successMsg.insert(0, ",恭喜您，数据已全部导入成功！共 " + mapInfos.size() + " 条");
        cargoRtnMsg.setReturnInfo(successMsg.toString());
        cargoRtnMsg.setMsgTime(new Date());
        return cargoRtnMsg;
    }


    @PostMapping("/deviceStatus")
    @ResponseBody
    @ApiOperation(value = "返回设备信息")
    @InterfaceLog(value = "设备信息查询", sender = "WMS", recipient = "WCS")
    public List<ReturnMsgDevice> deviceReceive(@RequestBody GetDeviceInfo msgGetDeviceBean) {
        log.info("接收到上位系统<设备信息>请求，参数-->" + JSON.toJSONString(msgGetDeviceBean));
        List<ReturnMsgDevice> devices = new ArrayList<ReturnMsgDevice>();
        Integer deviceId = msgGetDeviceBean.getDeviceNo();
        Integer deviceType = msgGetDeviceBean.getDeviceType();

        if (deviceId != null){
            deviceInfoService.getReturnMsgDeviceByDeviceId(devices,deviceId);
        }else if (deviceType != null){
            deviceInfoService.getReturnMsgDeviceByDeviceType(devices,deviceType);
        }

        return devices;
    }

}
